package com.xlongwei.accounts.controller;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.noear.snack.ONode;
import org.noear.solon.Utils;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Get;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.annotation.Param;
import org.noear.solon.annotation.Post;
import org.noear.solon.core.handle.Result;
import org.noear.solon.validation.annotation.Min;
import org.noear.solon.validation.annotation.NotBlank;
import org.noear.solon.validation.annotation.NotNull;
import org.noear.solon.validation.annotation.Pattern;
import org.noear.solon.validation.annotation.Valid;
import org.noear.solon.validation.annotation.Validated;
import org.smartboot.http.common.utils.CollectionUtils;

import com.xlongwei.accounts.dao.AccountDao;
import com.xlongwei.accounts.dao.ExtraDao;
import com.xlongwei.accounts.dao.LedgerDao;
import com.xlongwei.accounts.dao.MemberDao;
import com.xlongwei.accounts.dao.UserDao;
import com.xlongwei.accounts.model.Account;
import com.xlongwei.accounts.model.Extra;
import com.xlongwei.accounts.model.Ledger;
import com.xlongwei.accounts.model.Member;
import com.xlongwei.accounts.model.Record;
import com.xlongwei.accounts.model.User;
import com.xlongwei.accounts.service.RecordService;
import com.xlongwei.accounts.view.Daily;
import com.xlongwei.accounts.view.Overview;
import com.xlongwei.accounts.view.Query;
import com.xlongwei.accounts.view.Report;

import cn.dev33.satoken.annotation.SaCheckLogin;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Valid
@Controller
@SaCheckLogin
@Mapping("record")
public class RecordController {
    public static final String PATTERN_AMOUNT = "^\\d+(\\.\\d{1,2})?$";
    @Inject
    RecordService recordService;
    @Inject
    UserDao userDao;
    @Inject
    LedgerDao ledgerDao;
    @Inject
    MemberDao memberDao;
    @Inject
    AccountDao accountDao;
    @Inject
    ExtraDao extraDao;

    @Post
    @Mapping("create")
    public Result<?> create(@Param(defaultValue = "1") @Min(1) Integer ledgerid,
            @NotBlank @Pattern(PATTERN_AMOUNT) String amount,
            @Pattern(PATTERN_AMOUNT) String income,
            @Pattern(PATTERN_AMOUNT) String outcome,
            @Param(defaultValue = "1") @Min(1) Integer accountid_out,
            @Param(defaultValue = "1") @Min(1) Integer accountid_in,
            @Param(defaultValue = "1") @Min(1) Integer memberid, LocalDate day, String remark)
            {
        Integer fen = fen(amount);
        if (fen < 1) {
            return Result.failure("金额有误");
        }
        Integer userid = UserController.userid();
        Ledger ledger = ledgerDao.retrieve(ledgerid);
        if (ledger == null || (ledger.getUserid() != 0 && ledger.getUserid() != userid)) {
            return Result.failure("科目有误");
        }
        if ("I".equals(ledger.getType()) || "T".equals(ledger.getType())) {
            Account account = accountDao.retrieve(accountid_in);
            if (account == null || (account.getUserid() != 0 && account.getUserid() != userid)) {
                return Result.failure("账户有误");
            }
        }
        if ("O".equals(ledger.getType()) || "T".equals(ledger.getType())) {
            Account account = accountDao.retrieve(accountid_out);
            if (account == null || (account.getUserid() != 0 && account.getUserid() != userid)) {
                return Result.failure("账户有误");
            }
        }
        if (accountid_out.equals(accountid_in) && "T".equals(ledger.getType())) {
            return Result.failure("账户有误");
        }
        Member member = memberDao.retrieve(memberid);
        if (member == null || (member.getUserid() != 0 && member.getUserid() != userid)) {
            return Result.failure("成员有误");
        }
        day = day != null ? day : LocalDate.now();
        remark = handleRemark(userid, remark);
        Integer recordid = null;
        if ("I".equals(ledger.getType())) {
            recordid = recordService.income(userid, ledgerid, fen, accountid_in, memberid, day, remark);
        } else if ("O".equals(ledger.getType())) {
            recordService.outcome(userid, ledgerid, fen, accountid_out, memberid, day, remark);
        } else if ("T".equals(ledger.getType())) {
            if (4 == ledger.getLedgerid()) {
                recordid = recordService.charge(userid, fen, fen(income), accountid_out, accountid_in, memberid, day, remark);
            }else if (5 == ledger.getLedgerid()) {
                recordid = recordService.repay(userid, fen, fen(outcome), accountid_out, accountid_in, memberid, day, remark);
            } else {
                recordid = recordService.transfer(userid, ledgerid, fen, accountid_out, accountid_in, memberid, day, remark);
            }
        }
        return Result.succeed(recordid);
    }

    public boolean allowRemark(Integer userid) {
        User user = userDao.retrieve(userid);
        return user != null && user.getLevel() > 1;
    }

    public String handleRemark(Integer userid, String remark) {
        if (!Utils.isBlank(remark)) {
            if (allowRemark(userid)) {
                return remark.trim();
            }
        }
        return null;
    }

    @Get
    @Post
    @Mapping("retrieve")
    public Result<?> retrieve(@NotNull @Min(1) Integer recordid) {
        Record record = recordService.retrieve(recordid);
        ONode json = ONode.load(record);
        if (record != null) {
            json.set("amount", yuan(record.getAmount()));
            Integer userid = UserController.userid();
            if (allowRemark(userid)) {
                Extra extra = extraDao.retrive(recordid);
                if (extra != null && !Utils.isBlank(extra.getRemark())) {
                    json.set("remark", extra.getRemark());
                }
            }
            return Result.succeed(json);
        } else {
            return Result.failure("获取失败");
        }
    }

    @Post
    @Mapping("update")
    public Result<?> update(@NotNull @Min(1) Integer recordid, @Min(1) Integer ledgerid,
            @Pattern(PATTERN_AMOUNT) String amount, @Min(1) Integer accountid_out, @Min(1) Integer accountid_in,
            @Min(1) Integer memberid, LocalDate day, String remark) {
        Integer fen = Utils.isBlank(amount) ? null : fen(amount);
        if (fen != null && fen < 1) {
            return Result.failure("金额有误");
        }
        Integer userid = UserController.userid();
        Ledger ledger = ledgerDao.retrieve(ledgerid);
        if (ledger == null || (ledger.getUserid() != 0 && ledger.getUserid() != userid)) {
            return Result.failure("科目有误");
        }
        if ("I".equals(ledger.getType())) {
            accountid_out = 0;
        }
        if ("O".equals(ledger.getType())) {
            accountid_in = 0;
        }
        if ("I".equals(ledger.getType()) || "T".equals(ledger.getType())) {
            Account account = accountDao.retrieve(accountid_in);
            if (account == null || (account.getUserid() != 0 && account.getUserid() != userid)) {
                return Result.failure("账户有误");
            }
        }
        if ("O".equals(ledger.getType()) || "T".equals(ledger.getType())) {
            Account account = accountDao.retrieve(accountid_out);
            if (account == null || (account.getUserid() != 0 && account.getUserid() != userid)) {
                return Result.failure("账户有误");
            }
        }
        if (accountid_out.equals(accountid_in) && "T".equals(ledger.getType())) {
            return Result.failure("账户有误");
        }
        Member member = memberDao.retrieve(memberid);
        if (member == null || (member.getUserid() != 0 && member.getUserid() != userid)) {
            return Result.failure("成员有误");
        }
        remark = handleRemark(userid, remark);
        Record record = recordService.retrieve(recordid);
        if (record == null || record.getUserid().equals(userid) == false) {
            return Result.failure("操作失败");
        }
        Integer update = recordService.update(record, ledgerid, fen, accountid_out,
                accountid_in, memberid, day, remark);
        return Result.succeed(update);
    }

    public boolean isPartner(Integer userid, Integer partnerid) {
        if (partnerid == null || partnerid < 1) {
            return false;
        }
        User partner = userDao.retrieve(partnerid);
        if (partner == null) {
            return false;
        } else {
            User user = userDao.retrieve(userid);
            return user.getMobile().equals(partner.getPartner());
        }
    }

    @Get
    @Post
    @Mapping("report")
    public Result<?> report(@Validated Report report) {
        Integer userid = UserController.userid();
        if (CollectionUtils.isEmpty(report.getUserids())) {
            report.setUserids(Arrays.asList(userid));
        } else {
            List<Integer> userids = new ArrayList<>();
            if(report.getUserids().contains(userid)) userids.add(userid);
            for (Integer partnerid : report.getUserids()) {
                if (partnerid != userid && isPartner(userid, partnerid)) {
                    userids.add(partnerid);
                }
            }
            report.setUserids(userids);
        }
        report.setGroupBy(
                ("user".equals(report.getGroupBy()) && report.getUserids().size() < 2) ? ""
                        : report.getGroupBy());// 选择两个人时才能group by userid
        Overview overview = recordService.report(report);
        return Result.succeed(overview);
    }

    @Get
    @Post
    @Mapping("list")
    public Result<?> list(@Validated Query query) {
        Integer userid = UserController.userid();
        boolean isPartner = isPartner(userid, query.getUserid());
        log.info("isPartner={} userid={} query.userid={}", isPartner, userid, query.getUserid());
        if (isPartner == false) {
            query.setUserid(userid);
        }
        query.setRemark(handleRemark(userid, query.getRemark()));
        query.setWithRemark((query.isWithRemark() || !Utils.isBlank(query.getRemark())) && allowRemark(userid));
        query.setAmountMin(Utils.isBlank(query.getAmountMin()) ? null : String.valueOf(fen(query.getAmountMin())));
        query.setAmountMax(Utils.isBlank(query.getAmountMax()) ? null : String.valueOf(fen(query.getAmountMax())));
        List<Daily> list = recordService.list(query);
        return Result.succeed(list);
    }

    public static int fen(String yuan) {
        if (Utils.isBlank(yuan))
            return 0;
        int idx = yuan.indexOf('.');
        if (idx == -1) {
            return Integer.parseInt(yuan + "00");
        } else if (yuan.length() - idx == 2) {
            return Integer.parseInt(yuan.substring(0, idx) + yuan.substring(idx + 1) + "0");
        } else {
            return Integer.parseInt(yuan.substring(0, idx) + yuan.substring(idx + 1));
        }
    }

    public static String yuan(long fen) {
        if (fen > 0) {
            if (fen % 100 == 0) {
                return String.valueOf(fen / 100);
            } else if (fen % 10 == 0) {
                if (fen < 100) {
                    return "0." + fen / 10;
                } else {
                    return fen / 100 + "." + (fen % 100) / 10;
                }
            }
            String str = String.valueOf(fen);
            if (str.length() > 2) {
                return str.substring(0, str.length() - 2) + "." + str.substring(str.length() - 2);
            } else if (str.length() == 2) {
                return "0." + (str.endsWith("0") ? str.charAt(0) : str);
            } else {
                return "0.0" + str;
            }
        } else if (fen < 0) {
            return "-" + yuan(-fen);
        } else {
            return "0";
        }
    }
}
